/////////////////////////////////////////////////////////////////////
//  Package Description:
//    BBC iPlayer plugin for MythTV by Grant French (c) 2008-2009
//
//  Filename:  main.cpp
//  Author:    Grant French
//  Date:      06/12/2008
//
//  Description:
//    Main plugin file
//
// Based heavily on the mythprogramme plugin by
// (c) 2003-2007 Thor Sigvaldason and Isaac Richards
// Part of the MythTV project ( http://mythtv.org )
//
/////////////////////////////////////////////////////////////////////

#include <qapplication.h>

#include <mythtv/mythcontext.h>
#include <mythtv/lcddevice.h>
#include <mythtv/libmythui/myththemedmenu.h>
#include <mythtv/mythpluginapi.h>
#include <mythtv/mythmediamonitor.h>
#include <mythtv/util.h>

#include "programmetree.h"
#include "programmegallery.h"
#include "globalsettings.h"
#include "dbcheck.h"
#include "cleanup.h"
#include "globals.h"
#include "programmelist.h"
#include "programmeutils.h"
#include "parentalcontrols.h"
#include "iplayerupdate.h"

#if defined(AEW_VG)
#include <valgrind/memcheck.h>
#endif

namespace
{
    template <typename T>
    class screen_inst
    {
      public:
        screen_inst(T *inst, const QString &loc_name) : m_inst(inst),
            m_location_name(loc_name)
        {
        }

        int run()
        {
            gContext->addCurrentLocation(m_location_name);
            qApp->unlock();
            m_inst->exec();
            qApp->lock();
            gContext->removeCurrentLocation();
            return m_inst->programmeExitType();
        }

        ~screen_inst()
        {
            delete m_inst;
        }

      private:
        T *m_inst;
        const QString &m_location_name;
    };

    template <typename T>
    class q_screen_inst
    {
      public:
        q_screen_inst(T *inst, const QString &loc_name) : m_inst(inst),
            m_location_name(loc_name)
        {
        }

        int run()
        {
            gContext->addCurrentLocation(m_location_name);
            qApp->unlock();
            m_inst->exec();
            qApp->lock();
            gContext->removeCurrentLocation();
            return m_inst->programmeExitType();
        }

        ~q_screen_inst()
        {
            m_inst->deleteLater();
            m_inst = NULL;
        }

      private:
        T *m_inst;
        const QString &m_location_name;
    };

    template <typename T>
    int exec_screen(T *inst, const QString &loc_name)
    {
        screen_inst<T> si(inst, loc_name);
        return si.run();
    }

    template <typename T>
    int q_exec_screen(T *inst, const QString &loc_name)
    {
        q_screen_inst<T> si(inst, loc_name);
        return si.run();
    }

    class screens
    {
      private:
        static int runiPlayerUpdate()
        {

//           return q_exec_screen(
              new iPlayerUpdate();// gContext->GetMainWindow() ),
//               "iplayerupdate");
            return 0;
        }

        static int runProgrammeTree(ProgrammeList *programme_list)
        {
            return exec_screen(new ProgrammeTree(gContext->GetMainWindow(),
                                             "programmetree", "iplayer-",
                                             "programme tree", programme_list),
                               "programmelistings");
        }

        static int runProgrammeGallery(ProgrammeList *programme_list)
        {
            return q_exec_screen(new ProgrammeGallery(gContext->GetMainWindow(),
                                                  "programme gallery", programme_list),
                                 "programmegallery");
        }

      public:
        // FIXME: This silliness brought to you by REG_JUMP
        // which doesn't allow for basic things like pushing
        // an int or void * through the callback.
        enum screen_type
        {
            stProgrammeGallery = ProgrammeDialog::DLG_GALLERY,
            stProgrammeTree = ProgrammeDialog::DLG_TREE,
            stiPlayerUpdate,
            stDefaultView
        };

        static void runScreen(screen_type st)
        {
            static ProgrammeList *programme_list = 0;

            if (st == stDefaultView)
            {
                st = static_cast<screen_type>(
                        gContext->GetNumSetting("Default MythiPlayer View",
                                                stProgrammeGallery));
                if (!ProgrammeDialog::IsValidDialogType(st))
                    st = stProgrammeGallery;
            }

            if (!programme_list)
            {
                programme_list = new ProgrammeList;
            }

            int sret = 0;
            switch (st)
            {
                case stiPlayerUpdate:
                {
                    sret = runiPlayerUpdate();
                    break;
                }
                case stProgrammeTree:
                {
                    sret = runProgrammeTree(programme_list);
                    break;
                }
                case stProgrammeGallery:
                {
                    sret = runProgrammeGallery(programme_list);
                    break;
                }
                case stDefaultView:
                default:
                {
                    sret = runProgrammeGallery(programme_list);
                    break;
                }
            }

            if (sret != SCREEN_EXIT_VIA_JUMP)
            {
                // All these hoops to make view switching faster.

                // If a screen didn't exit via a jump we should clean up
                // the data we loaded.
                CleanupHooks::getInstance()->cleanup();
                delete programme_list;
                programme_list = 0;
            }
        }
    };

    void screeniPlayerUpdate() { screens::runScreen(screens::stiPlayerUpdate); }
    void screenProgrammeTree() { screens::runScreen(screens::stProgrammeTree); }
    void screenProgrammeGallery() { screens::runScreen(screens::stProgrammeGallery); }
    void screenProgrammeDefault() { screens::runScreen(screens::stDefaultView); }


    ///////////////////////////////////////
    // MythiPlayer functions
    ///////////////////////////////////////

    void setupKeys()
    {
        REG_JUMP(JUMP_IPLAYER_DEFAULT, "The MythiPlayer default view", "",
                 screenProgrammeDefault);

        REG_JUMP(JUMP_IPLAYER_UPDATE, "The MythiPlayer updater", "",
                 screeniPlayerUpdate);
        REG_JUMP(JUMP_IPLAYER_TREE, "The MythiPlayer programme listings", "",
                 screenProgrammeTree);
        REG_JUMP(JUMP_IPLAYER_GALLERY, "The MythiPlayer programme gallery", "",
                 screenProgrammeGallery);

        REG_KEY("Programme","FILTER","Open programme filter dialog","F");

        REG_KEY("Programme","DELETE","Delete programme","D");
        REG_KEY("Programme","BROWSE","Change browsable in programme manager","B");
        REG_KEY("Programme","INCPARENT","Increase Parental Level","],},F11");
        REG_KEY("Programme","DECPARENT","Decrease Parental Level","[,{,F10");

        REG_KEY("Programme","HOME","Go to the first programme","Home");
        REG_KEY("Programme","END","Go to the last programme","End");

    }

    void ProgrammeCallback(void *data, QString &selection)
    {
        (void)data;

        QString sel = selection.lower();

        if (sel == "update")
            screeniPlayerUpdate();
        else if (sel == "listing")
            screenProgrammeTree();
        else if (sel == "gallery")
            screenProgrammeGallery();
        else if (sel == "settings_general")
        {
            //
            //  If we are doing aggressive
            //  Parental Control, then junior
            //  is going to have to try harder
            //  than that!
            //

            if (gContext->GetNumSetting("ProgrammeAggressivePC", 0))
            {
                if (checkParentPassword(ParentalLevel::plHigh))
                {
                    ProgrammeGeneralSettings settings;
                    settings.exec();
                }
            }
            else
            {
                ProgrammeGeneralSettings settings;
                settings.exec();
            }
        }
        else if (sel == "settings_player")
        {
            ProgrammePlayerSettings settings;
            settings.exec();
        }

    }

    void runMenu(const QString &menuname)
    {
        QString themedir = gContext->GetThemeDir();

        MythThemedMenu *diag =
                new MythThemedMenu(themedir.ascii(), menuname,
                                   GetMythMainWindow()->GetMainStack(),
                                   "programme menu");

        diag->setCallback(ProgrammeCallback, NULL);
        diag->setKillable();

        if (diag->foundTheme())
        {
            if (LCD *lcd = LCD::Get())
            {
                lcd->switchToTime();
            }
            GetMythMainWindow()->GetMainStack()->AddScreen(diag);
        }
        else
        {
            VERBOSE(VB_IMPORTANT,
                    QString("Couldn't find theme %1").arg(themedir));
            delete diag;
        }
    }
}

int mythplugin_init(const char *libversion)
{
    if (!gContext->TestPopupVersion("mythiplayer", libversion,
                                    MYTH_BINARY_VERSION))
        return -1;

    gContext->ActivateSettingsCache(false);
    UpgradeiPlayerDatabaseSchema();
    gContext->ActivateSettingsCache(true);

    ProgrammeGeneralSettings general;
    general.load();
    general.save();

    ProgrammePlayerSettings settings;
    settings.load();
    settings.save();

    setupKeys();

    return 0;
}

int mythplugin_run()
{
    runMenu("iplayer_menu.xml");
    return 0;
}

int mythplugin_config()
{
    runMenu("iplayer_settings.xml");

    return 0;
}

void mythplugin_destroy()
{
    CleanupHooks::getInstance()->cleanup();
#if defined(AEW_VG)
    // valgrind forgets symbols of unloaded modules
    // I'd rather sort out known non-leaks than piece
    // together a call stack.
    VALGRIND_DO_LEAK_CHECK;
#endif
}
